﻿using System.Windows.Documents;
using System.Windows.Media;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Indentation;
using ICSharpCode.AvalonEdit.Indentation.CSharp;
using Poet.ViewModels;

namespace Poet.Modules
{
    /// <summary>
    /// Text Editor Extensions.
    /// </summary>
    internal static class TextEditorExtensions
    {
        /// <summary>
        /// Gets the format.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <returns></returns>
        public static FormatSet GetCommon(this DocumentViewModel model)
        {
            return new FormatSet
                {
                    ShowLineNumbers = model.TextEditorViewModel.ShowLineNumbers,
                    ShowTabs = model.TextEditorViewModel.TextArea.Options.ShowTabs,
                    ShowSpaces = model.TextEditorViewModel.TextArea.Options.ShowSpaces,
                    ShowBoxeForControlCharacters = model.TextEditorViewModel.TextArea.Options.ShowBoxForControlCharacters,
                    ShowEndOfLines = model.TextEditorViewModel.TextArea.Options.ShowEndOfLine,
                    WordWrap = model.TextEditorViewModel.WordWrap,
                    TextFolding = model.UseFolding,
                    BracketComplete = model.CloseBrackets,
                    WordComplete = model.UseAutocomplete,
                    SpellCheck = model.UseSpell,
                    SyntaxHightlight = model.UseHighlighting,
                    HideCurrentLineWhenTextSelected = model.TextEditorViewModel.TextArea.HideCurrentLineWhenTextSelected,
                };
        }

        /// <summary>
        /// Sets the format.
        /// </summary>
        /// <param name="model">The model.</param>
        /// <param name="format">The format.</param>
        public static void SetCommon(this DocumentViewModel model, FormatSet format)
        {
            model.TextEditorViewModel.ShowLineNumbers = format.ShowLineNumbers;
            model.TextEditorViewModel.TextArea.Options.ShowTabs = format.ShowTabs;
            model.TextEditorViewModel.TextArea.Options.ShowSpaces = format.ShowSpaces;
            model.TextEditorViewModel.TextArea.Options.ShowBoxForControlCharacters = format.ShowBoxeForControlCharacters;
            model.TextEditorViewModel.TextArea.Options.ShowEndOfLine = format.ShowEndOfLines;
            model.TextEditorViewModel.WordWrap = format.WordWrap;
            model.UseFolding = format.TextFolding;
            model.CloseBrackets = format.BracketComplete;
            model.UseAutocomplete = format.WordComplete;
            model.UseSpell = format.SpellCheck;
            model.UseHighlighting = format.SyntaxHightlight;
        }

        /// <summary>
        /// Sets the syntax.
        /// </summary>
        /// <param name="textArea">The text area.</param>
        /// <param name="syntaxName">The syntax.</param>
        public static void SetSyntax(this TextArea textArea, string syntaxName)
        {
            if (syntaxName.Contains("ML"))
            {
                textArea.IndentationStrategy = new DefaultIndentationStrategy();
                textArea.FoldingStrategy = new XmlFoldingStrategy();
            }
            else if (syntaxName.Equals("C#") || syntaxName.Equals("C++") ||
                     syntaxName.Equals("PHP") || syntaxName.Equals("Java"))
            {
                textArea.IndentationStrategy = new CSharpIndentationStrategy(textArea.Options);
                textArea.FoldingStrategy = new BraceFoldingStrategy();
            }
            else
            {
                textArea.IndentationStrategy = new DefaultIndentationStrategy();
                textArea.FoldingStrategy = null;
            }
        }

        /// <summary>
        /// Selects the substring and make visible.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <param name="startIndex">The start index.</param>
        /// <param name="length">The length.</param>
        public static void SelectSubstringAndMakeVisible(this TextEditor textEditor, int startIndex, int length)
        {
            textEditor.Select(startIndex, length);
            textEditor.ScrollTo(textEditor.TextArea.Caret.Line, textEditor.TextArea.Caret.Column - textEditor.SelectionLength);
        }

        /// <summary>
        /// Sets the font.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <param name="font">The font.</param>
        public static void SetFont(this TextEditor textEditor, FontSet font)
        {
            textEditor.FontFamily = new FontFamily(font.FontFamily);
            textEditor.FontSize = font.FontSize;
            textEditor.FontStretch = font.FontStretch;
            textEditor.FontStyle = font.FontStyle;
            textEditor.FontWeight = font.FontWeight;
        }

        /// <summary>
        /// Gets the font.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <returns></returns>
        public static FontSet GetFont(this TextEditor textEditor)
        {
            return new FontSet
                {
                    FontFamily = textEditor.FontFamily.ToString(),
                    FontSize = textEditor.FontSize,
                    FontStretch = textEditor.FontStretch,
                    FontStyle = textEditor.FontStyle,
                    FontWeight = textEditor.FontWeight,
                };
        }

        /// <summary>
        /// Sets the palette.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <param name="set">The brushes.</param>
        public static void SetPalette(this TextEditor textEditor, PaletteSet set)
        {  
            textEditor.Foreground = set.Foreground;
            textEditor.Background = set.Background;
            textEditor.TextArea.SelectionForeground = set.SelectedText;
            textEditor.TextArea.SelectionBrush = set.SelectionBrush;
            textEditor.TextArea.AccentuateBrush = set.AccentuateBackground;
            textEditor.TextArea.SelectionBorder = set.SelectionBorder;
            textEditor.TextArea.AccentuateBorder = set.AccentuateBorder;
            textEditor.TextArea.CurrentLineBrush = set.CurrentLineBackground;
            textEditor.TextArea.CurrentLineBorder = set.CurrentLineBorder;
            textEditor.TextArea.LineNumberForeground = set.LineNumberForeground;
            textEditor.TextArea.LineNumberSeparator = set.LineNumberSeparator;

            textEditor.TextArea.AccentuateCornerRadius = set.AccentuateBorderCornerRadius;
            textEditor.TextArea.CurrentLineCornerRadius = set.CurrentLineBorderCornerRadius;
            textEditor.TextArea.SelectionCornerRadius = set.SelectionBorderCornerRadius;
        }

        /// <summary>
        /// Gets the palette.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <returns></returns>
        public static PaletteSet GetPalette(this TextEditor textEditor)
        {
            return new PaletteSet
                {
                    Foreground = textEditor.Foreground,
                    Background = textEditor.Background,
                    SelectedText = textEditor.TextArea.SelectionForeground,
                    SelectionBrush = textEditor.TextArea.SelectionBrush,
                    SelectionBorder = textEditor.TextArea.SelectionBorder,
                    CurrentLineBackground = textEditor.TextArea.CurrentLineBrush,
                    CurrentLineBorder = textEditor.TextArea.CurrentLineBorder,
                    LineNumberForeground = textEditor.TextArea.LineNumberForeground,
                    LineNumberSeparator = textEditor.TextArea.LineNumberSeparator,
                    AccentuateBackground = textEditor.TextArea.AccentuateBrush,
                    AccentuateBorder = textEditor.TextArea.AccentuateBorder,

                    AccentuateBorderCornerRadius = textEditor.TextArea.AccentuateCornerRadius,
                    CurrentLineBorderCornerRadius = textEditor.TextArea.CurrentLineCornerRadius,
                    SelectionBorderCornerRadius = textEditor.TextArea.SelectionCornerRadius,
                };
        }

        /// <summary>
        /// Creates the flow document for editor.
        /// </summary>
        /// <param name="textEditor">The text editor.</param>
        /// <returns></returns>
        public static FlowDocument CreateFlowDocument(this TextEditor textEditor)
        {
            var highlighter = textEditor.TextArea.GetService(typeof (IHighlighter)) as IHighlighter;
            return new FlowDocument(ConvertTextDocumentToBlock(textEditor.Document, highlighter))
                       {FontFamily = textEditor.FontFamily, FontSize = textEditor.FontSize};
        }

        private static Block ConvertTextDocumentToBlock(TextDocument document, IHighlighter highlighter)
        {
            var paragraph = new Paragraph();
            foreach (var line in document.Lines)
            {
                var inlineBuilder = new HighlightedInlineBuilder(document.GetText(line));
                if (highlighter != null)
                {
                    var highlightedLine = highlighter.HighlightLine(line.LineNumber);
                    foreach (var section in highlightedLine.Sections)
                        inlineBuilder.SetHighlighting(section.Offset - line.Offset, section.Length, section.Color);
                }
                paragraph.Inlines.AddRange(inlineBuilder.CreateRuns());
                paragraph.Inlines.Add(new LineBreak());
            }
            return paragraph;
        }
    }
}
